<?php
/**
 * Класс пост-обработки поля форм-процессора. Пост-обработка и валидация разные вещи. Главное отличие в том что в
 * пост-обработке идет работа с внешними хранилищами, а при валидации проверяетя лишь правильность ввода на уровне синтаксиса.
 *
 * @package AggregatedModel
 * @author Alexander Sibiryakov
 **/
class Model_Field_Processor
{
	var $error_num;

	var $results;

	public function __construct(RL_SiteRequestHandler $rh, Model_AclAssistant $acl_ass, $item_oid=NULL)  {
		$this->rh = &$rh;
		$this->acl_ass = &$acl_ass;
		if ($item_oid != NULL) $this->item_oid = $item_oid;
	}

	public function error_msg() {
		switch($this->error_num) {
			case FIELDP_PID_PROFILE_NOTRESOLVED:
			return "Адрес URL автора недоступен.";
			break;

			case FIELDP_UPLOAD_ERR:
			return "Ошибка при загрузке файла.";
			break;

			case FIELDP_OK:
			return "Нормальное завершение";
			break;

			case FIELDP_EMPTY_URL:
			return "URL пользователя пуст.";
			break;

			case FIELDP_UNKWONW_TUBE:
			return "Указанный видеохостинг не поддерживается.";
			break;

			default:
			break;
		}
	}

	public function get_last_result( $field_name ) {
		// тут лежит результат последней функции
		return $this->results[ $field_name ];
	}

	private function return_me( $field_name, $value ) {
		// поидее использовать там где нужны вернуть значение внутри функции
		$this->results[ $field_name ] = $value;
		return $value;
	}

	private function process_platonicaid($field_config, $field_name, $value) {
		// тут мы поидее должны обратиться к сервису Димы Копытина и сфетчить оттуда URL адрес,
		// а затем его распарсить и положить к себе в бд

		if (trim($value)=="") {
			$this->return_me($field_name, NULL);
			return true;
		}

		$this->rh->UseClass("Model_PlatonicaIDuser");
		$model = new Model_PlatonicaIDuser($this->rh);
		$data = $model->lookupUrl( $value );

		if (is_array($data)) {
			$newuser = $model->createUser($data);
			$this->return_me($field_name, $newuser->id);
			return true;

		} else {
			$this->error_num = FIELDP_PID_PROFILE_NOTRESOLVED;
			return false;
		}

		return false;
	}

	private function process_url($field_config, $field_name, $value) {
		if (trim($value)=="") {
			$this->return_me($field_name, $value);
			return true;
		}

		preg_match("/(http|https|ftp):\/\//", $value, $matches);
		if (!isset($matches[0])) {
			$this->return_me($field_name, "http://".$value);
			return true;
		} else {
			$this->return_me($field_name, $value);
			return true;
		}
	}

	private function process_videotubelink($field_config, $field_name, $value)
  {
		$thing = trim($value);

		if (!$thing) {
			$this->return_me($field_name, $value);
			return true;
		}

		// lucky
		// из tinymce/plugins/media/js/media.js

		// YouTube
		// http://www.youtube.com/watch?v=LLhRtV1xKiM
		if (preg_match('#watch\?v=.+#', $thing)) {
			if (preg_match('#[?&]v=([^&]+)(&|$)#', $thing, $ms)) {
				$width = 425;
				$height = 350;
				$serial = $ms[1];
				$src = "http://www.youtube.com/v/{$serial}";

				// common html (as generated by tinymce)
				$tube_mce_html = <<<EOD
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="{$width}" height="{$height}" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="src" value="{$src}" /><embed type="application/x-shockwave-flash" width="{$width}" height="{$height}" src="{$src}"></embed></object>
EOD;
				$tube_html = $tube_mce_html;

				$this->return_me($field_name, $value);
				$this->return_me('tube_html', $tube_html);
				$this->return_me('tube_mce_html', $tube_mce_html);
				return true;
			}
		}

		// FIXME : 2008-09-19 : lucky
		//
		// video.google.com также публикует контент с www.youtube.com
		// такой контент не отображается плеером google.
		//
		// в браузере ссылка открывает страницу, где во фреме отображается www.youtube.com
		// например ссылка http://video.google.com/videoplay?docid=-7031840825797383424&vt=lf
		// открывает фрем с http://www.youtube.com/watch?v=nmniBnVB6wA внутри.
		//
		// плеер, сгенерированный для http://video.google.com/googleplayer.swf выдает:
		// This video is currently not
		//        available.
		// Please try again later.
		//
		// вот еще подобная ссылка:
		// http://video.google.com/googleplayer.swf?docId=1572013877568890999&vt=lf&hl=en
		//
		// а вот такая ссылка работает:
		// http://video.google.com/videoplay?docid=4244229217435028626&hl=en

		// Google video
		$url = 'http://video.google.com/videoplay?docid=';
		if ($url === substr($thing, 0, strlen($url))) {
			$width = 425;
			$height = 326;
			$serial = substr($thing, strlen($url));
			$src = "http://video.google.com/googleplayer.swf?docId={$serial}&hl=en";

			// common html (as generated by tinymce)
			$tube_mce_html = <<<EOD
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="{$width}" height="{$height}" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="src" value="{$src}" /><embed type="application/x-shockwave-flash" width="{$width}" height="{$height}" src="{$src}"></embed></object>
EOD;
			$tube_html = $tube_mce_html;

			$this->return_me($field_name, $value);
			$this->return_me('tube_html', $tube_html);
			$this->return_me('tube_mce_html', $tube_mce_html);
			return true;
		}

		// RuTube
		// http://rutube.ru/tracks/1019341.html?v=694792b6c290ab4a6dcc397d61fab257
		$url = 'http://rutube.ru/tracks/';
		if ($url === substr($thing, 0, strlen($url))) {
			if (preg_match('#[?&]v=([^&]+)(&|$)#', $thing, $ms)) {
				$width = 470;
				$height = 353;
				$serial = $ms[1];
				$src = "http://video.rutube.ru/{$serial}";

				// common html (as generated by tinymce)
				$tube_mce_html = <<<EOD
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="{$width}" height="{$height}" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="src" value="{$src}" /><embed type="application/x-shockwave-flash" width="{$width}" height="{$height}" src="{$src}"></embed></object>
EOD;

				// custom html from rutube
				$tube_html = <<<EOD
<OBJECT width="{$width}" height="{$height}"><PARAM name="movie" value="{$src}"></PARAM><PARAM name="wmode" value="window"></PARAM><PARAM name="allowFullScreen" value="true"></PARAM><PARAM name="flashVars" value="uid=1925"></PARAM><EMBED src="{$src}" type="application/x-shockwave-flash" wmode="window" width="{$width}" height="{$height}" allowFullScreen="true" flashVars="uid=1925"></EMBED></OBJECT>
EOD;


				$this->return_me($field_name, $value);
				$this->return_me('tube_html', $tube_html);
				$this->return_me('tube_mce_html', $tube_mce_html);
				return true;
			}
		}

		$this->return_me($field_name, $value);
		$this->error_num = FIELDP_UNKWONW_TUBE;
		return false;
	}

	private function process_image($field_config, $field_name, $value)
	{
		// Тут создается объект ImageStorage, который и сохранит картинку нужным образом

		// отработаем, когда изображение не пришло
		if ($_FILES['_'.$field_name]['error'] == 4) {
			$this->return_me($field_name, $value);
			return true;
		}

		if ($_FILES['_'.$field_name]['error'] == 0)  {
			$this->rh->UseClass('stores_ImageStorage');
			$img_obj = new ImageStorage( $this->rh->ctx->store );

			if (is_numeric($value) && (trim($value)!="")) {
				// 1. проверим пришел-не пришел файл
				if ($this->rh->FILES['_'.$field_name]['tmp_name'] != "") {
					// пришел
					// 2. удалим файлы и запись
					$img_obj->Del( $image_oid );
				} else {
					// не пришел
				}
			}

			$thumbs = array( // в этом массиве если одно из разрешений не задано, то по нему нет ограничений
				'orig' => array(
					'filter' => 'pass',
					 'clone' => false,
				),
				'sm1' => array(
					'filter' => 'scale',
					 'max_w' => 220,
					 'max_h' => 220,
				),
				"sm2" => array(
					'filter' => 'scale',
					"max_w" => 460,
				),
				"gal"=>array(
					'filter' => 'scale',
					"w" => 100,
					"h" => 100,
				),
				"wdg"=>array(
					'filter' => 'scale',
					"w" => 53,
					"h" => 53,
				),
			);

			$image_oid = $img_obj->Put($this->rh->FILES['_'.$field_name]['tmp_name'], $thumbs);

			$data[ $field_config['db_name'] ] = $image_oid;
			$this->return_me($field_name, $image_oid);
			return true;
		} else {
			$this->error_num = FIELDP_UPLOAD_ERR;
			return false;
		}

	}

	private function process_file($field_config, $field_name, $value) {
		// Аплоад файла используя функцию хранилища

		// отработаем, когда юзер не выбрал файл
		if ($_FILES['_'.$field_name]['error'] == 4) {
			$this->return_me($field_name, $value);
			return true;
		}

		// если файл пришел
		if ($_FILES['_'.$field_name]['error'] == 0)  {

			// 2. удалить файл сам и объект, если нам на них указали
			if (is_numeric($value) && (trim($value)!="")) {
				$_res = $this->rh->ctx->store->find("objects where objects.oid==".$value);
				$file_object = $_res->next();
				if ($file_object instanceof fobject) {
					unlink($file_object->data['path_to_file']);
					$file_object->delete('filestorage');
				}
			}

			// добавляем новый файл
			$dest_name = $this->rh->ctx->store->addFile( $this->rh->FILES['_'.$field_name]['tmp_name'] );
			$real_fname = $this->rh->FILES['_'.$field_name]['name'];
			$ext = mb_substr( $real_fname, mb_strrpos( $real_fname, "." ) + 1 ); // расширение для разных сортировок

			$newfile = $this->rh->ctx->store->create('ffilestorage', array(
				'path_to_file'=>$dest_name,
				'mime_type'=>$this->rh->FILES['_'.$field_name]['type'],
				'file_size'=>$this->rh->FILES['_'.$field_name]['size'],
				'real_fname'=>$this->rh->FILES['_'.$field_name]['name'],
				'ext'=>$ext,
				)  );
			$newfile->save();
			$newfile->inSense('filestorage');
			$this->return_me($field_name, $newfile->id);
			return true;
		} else {
			$this->error_num = FIELDP_UPLOAD_ERR;
			return false;
		}

	}

	public function process_safehtml($field_config, $field_name, $value) {
		define('XML_HTMLSAX3', "safehtml/classes/");
		$this->rh->UseClass("safehtml_classes_safehtml");

		$safehtml =& new safehtml();
		$this->return_me($field_name, $safehtml->parse( $value ));
		return true;
	}

	public function process_tags($field_config, $field_name, $value) {
		// --- tags --- {{{

		// создадим ассоциацию тега (в сообществе) и только что появившегося на свет итема
		// если тега нет, создадам и его
		// сообщество есть всегда
		$_community_oid = $this->acl_ass->acl_data['comity_oid'];

		// убъем все связи объекта с тегами ...
		$sql = "DELETE FROM store_tag_subject WHERE subject_oid = {$this->item_oid}";
		$this->rh->ctx->store->db->query($sql);

		// ... и создадим новые
		// !! далее c&p из CreateItem
		require_once 'includes/tags.php';
		$atags = tags_split($value);
		foreach ($atags as $tag_name) {
			// ищем первый попавшийся тег по имени и сообществу
			$tag = $this->rh->ctx->store->find
				(
					'select objects where tag.name=:0 AND tag.community_oid=:1',
					array($tag_name, $_community_oid)
				)->next();
			if (empty($tag)) {
			// если такого тэга нет -- рожаем
				$tag_info = array(
					'name' => $tag_name,
					'community_oid'=>$_community_oid
				);
				$tag = $this->rh->ctx->store->create ('ftag', $tag_info);
				$tag->save();
				$tag->inSense('tag');
			}
			// теперь родим ассоциацию итема и тэга

			$sql =
				"INSERT INTO store_tag_subject (tag_oid, subject_oid)
				VALUES ({$tag->id}, {$this->item_oid})";
			$this->rh->ctx->store->db->query($sql);
		}
		//
		// --- tags ---  }}}
		$this->return_me($field_name, $value);
		return true;

	}

	var $I_AM = 'Я';
	public function process_authors ($field_config, $field_name, $value)
	{
		// список авторов документов вводится пользователем по правилам формирования
		// списка тегов. Далее авторы либо привязываются к существующим профилям,
		// либо создается профиль. )
		// в качестве авторов мы вибираем профили пользователей в контексте данного
		// сообщества.

		$_community_oid = $this->acl_ass->acl_data['comity_oid'];

		// убъем все связи объекта с авторами ...
		$db = $this->rh->ctx->store->db;
		$prefix = $db->db_prefix;
		$sql = "DELETE FROM {$prefix}item_author WHERE item_oid = {$this->item_oid}";
		$db->query($sql);


		// ... и создадим новые
		// разбираем исходную строку как список тегов
		require_once 'includes/tags.php';
		$atags = tags_split($value);
		$m_person = $this->rh->ctx->mm->getModel('Person', 'm');
		$authors = array();

		foreach ($atags as $k => $person_title) {
			$author = array();
			$person = NULL;

			// спец-тэг "Я", когда юзер хочет назваться автором.
			if (!$person && mb_strtoupper($person_title) === $this->I_AM) {
				$user_oid = $this->acl_ass->acl_data['user']['user_id'];
				$person = $this->rh->ctx->mm->getModel('User', 'm')->getUser($user_oid);
				if ($person) { // на самом деле есть такой
					$person_title = $person->data['fullname'];
					// .. и в $value прописать fullname (oid)
					// "Я, Василий Иванович" => "Петька (323), Василий Иванович"
					$atags = tags_split($value);
					$atags[$k] = $person->data['fullname']." ({$person->id})";
					$value = implode(", ", $atags);
				}
			}

			// заголовок содержит oid персоны в скобках "Петька (323)"
			if (!$person && preg_match_all('#\(([[:digit:]]+)\)#', $person_title, $ms, PREG_SET_ORDER)) { // похоже получили oid персоны
        $person_oid = $ms[count($ms)-1][1]; // FIXME : 2008-06-11 : lucky
                                            // пытаюсь защититься от возможных неприятностей, когда fullname автора
                                            // содержит скобки с цифрами "Петька(12)". По идее должны получить
                                            // "Петька(12) (323)", -- oid в скобках стоит в конце
                                            // тогда берем последнее (правое) совпадение,
				$person = $m_person->getPerson($person_oid);
				if ($person) { // на самом деле есть такой
					$person_title = str_replace($ms[0], '', $person_title);
				}
			}

			// так-и неизвестно кто
			if (!$person) { // нужно создать.
				$community_oid = $this->acl_ass->acl_data['comity_oid'];
				$person = $m_person->createPerson($community_oid, array(
					'fullname' => $person_title,
				));
				if (!$m_person->putPerson($person)) {
					error (__METHOD__. " can't to create person for '$person_title'");
					unset($person);
					return false;
				}

				// .. и в $value прописать oid
				// "Петька, Василий Иванович" => "Петька (323), Василий Иванович"
				$atags = tags_split($value);
				$atags[$k] .= " ({$person->id})";
				$value = implode(", ", $atags);
			}

			// тут $person должен быть определён.
			// рожаем ассоциацию итема с этим автором
			// а в инфу об авторе сохраняем person_oid
			$sql =
				"INSERT INTO {$prefix}item_author (item_oid, author_oid)
				VALUES ({$this->item_oid}, {$person->id})";
			$db->query($sql);
			$author['oid'] = $person->id;
			$author['fullname'] = trim($person_title);
			$authors[] = $author;
		}

		$this->return_me($field_name, $value);
		$this->return_me(ucfirst($field_name), $authors);
		return true;
	} // end of method process_authors

	// здесь живет обработка поля. обычно все поля обрабатываются тупо копированием значения,
	// но с полями типа аплоад и url профайла дело сложней
	public function processField($field_config, $field_name, $value) {

		$this->error_num = 0;

		if (isset($field_config["external_postprocessing"])) {
			// это поле аплоад
			// на выходе нужно oid объекта, который потом будет пониматься каким нибудь обработчиком вывода
			$method = "process_".$field_config["external_postprocessing_type"];
			debug (__METHOD__ . " --- $method");
			$result = call_user_func_array(array($this, $method), array($field_config, $field_name, $value));
			if ($result === false) {
				return false;
			} else {
				return true;
			}
		}
		$this->return_me($field_name, $value);
		return true;
	}

	/**
	 * getResults вернуть результаты обработки полей.
	 *
	 * @return array
	 */
	public function getResults ()
	{
		return $this->results;
	} // end of method getResults
	/**
	 * cleanResults очистить результаты обработки полей.
	 *
	 * @return $this
	 */
	public function cleanResults ()
	{
		$this->results = array();
		return $this;
	} // end of method cleanResults
}
?>
